/**
 *
 */
package org.tangram.component.base;

import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang.ClassUtils;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import org.simpleframework.xml.stream.InputNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tangram.component.Component;
import org.tangram.component.ComponentManager;
import org.tangram.component.ComponentState;
import org.tangram.component.Extension;
import org.tangram.component.ExtensionPoint;
import org.tangram.component.xml.ComponentDefintion;
import org.tangram.component.xml.DefintionManager;
import org.tangram.component.xml.DependencyDefintion;
import org.tangram.component.xml.ExtensionDefintion;
import org.tangram.component.xml.ExtensionPointDefintion;

/**
 * 缺省的构件管理器实现类
 * 
 * @author han_feng
 * 
 */
public class BaseComponentManager extends ComponentManager {

	private static final Logger logger = LoggerFactory
			.getLogger(BaseComponentManager.class);

	private static final String XML_FILE_PATH = "META-INF/tangram/component.xml";

	/**
	 * 对currentComponent进行依赖分析，先将其依赖添加到distMap中，然后加入currentComponent
	 * 
	 * @param currentComponent
	 *            当前待分析构件
	 * @param componentMap
	 *            用于检索构件的Map
	 * @param distMap
	 *            排序后目标容器
	 */
	private static void sortByDependencies(BaseComponent currentComponent,
			Map<String, BaseComponent> componentMap,
			Map<String, BaseComponent> distMap) {

		if (distMap.containsKey(currentComponent.getKey()))
			return;

		BaseComponent component;
		boolean isMissingDependency = false;
		for (DependencyDefintion depend : currentComponent.getDefintion()
				.getDependencyDefintions()) {
			component = componentMap.get(depend.getKey());
			if (component != null) {
				if (component.getState() == ComponentState.MISSING
						|| component.getState() == ComponentState.MISSING_DEPENDENCY) {
					isMissingDependency = true;
				} else
					sortByDependencies(component, componentMap, distMap);
			} else {
				// 依赖不存在
				component = new BaseComponent(depend.getGroupId(),
						depend.getId(), depend.getVersion());
				component.setState(ComponentState.MISSING);

				distMap.put(component.getKey(), component);

				isMissingDependency = true;

				logger.error("构件\"{}\"的依赖项\"{}\"不存在，系统可能无法正常使用！",
						currentComponent.getKey(), depend.getKey());
			}
		}
		if (isMissingDependency)
			currentComponent.setState(ComponentState.MISSING_DEPENDENCY);

		distMap.put(currentComponent.getKey(), currentComponent);
	}

	private Map<String, BaseComponent> componentIndexMap;

	private Map<String, ExtensionPoint> extensionPointMap = new ConcurrentHashMap<String, ExtensionPoint>();

	private Map<String, List<Extension>> extensions = new ConcurrentHashMap<String, List<Extension>>();

	private DefintionManager defintionManager;

	private Serializer serializer = new Persister();

	public BaseComponentManager() throws Exception {

		Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
				.getResources(XML_FILE_PATH);

		defintionManager = new DefintionManager();

		ComponentDefintion componentDefintion;
		Map<String, BaseComponent> componentMap = new LinkedHashMap<String, BaseComponent>();
		BaseComponent component;

		// 加载构件
		while (urls.hasMoreElements()) {
			componentDefintion = defintionManager.loadFromXML(urls
					.nextElement());
			if (componentDefintion == null)
				continue;

			component = new BaseComponent(componentDefintion);
			componentMap.put(component.getKey(), component);

			// 初始化并索引扩展点定义
			for (ExtensionPointDefintion epd : componentDefintion
					.getExtensionPointDefintions()) {

				epd.setOwnerComponent(component);

				if (extensionPointMap.containsKey(epd.getId())) {
					// 重复扩展点定义检测，给出警告
					if (logger.isWarnEnabled()) {
						String key1 = ((BaseComponent) extensionPointMap.get(
								epd.getId()).getOwnerComponent()).getKey();
						logger.warn(
								"在构件\"{}\"和构件\"{}\"中定义了相同名称的扩展点\"{}\"！",
								new String[] {
										key1,
										((BaseComponent) epd
												.getOwnerComponent()).getKey(),
										epd.getId() });
					}
				} else
					extensionPointMap.put(epd.getId(), epd);
			}

		}

		// 按照依赖关系排序构件
		this.componentIndexMap = new LinkedHashMap<String, BaseComponent>();
		for (BaseComponent comp : componentMap.values()) {
			sortByDependencies(comp, componentMap, this.componentIndexMap);
		}

		// 处理扩展点实现
		String point;
		for (BaseComponent comp : this.componentIndexMap.values()) {
			// 按照依赖关系顺序处理扩展点实现
			for (ExtensionDefintion extensionDefintion : comp.getDefintion()
					.getExtensionDefintions()) {
				point = extensionDefintion.getPoint();
				if (extensionPointMap.containsKey(point))
					addExtension(extensionDefintion, comp);
				else
					logger.warn("扩展点{}未定义，构件{}中存在无效的扩展配置项", point,
							comp.getKey());
			}
		}

	}

	@SuppressWarnings("unchecked")
	private void addExtension(ExtensionDefintion extensionDefintion,
			BaseComponent provider) {

		String point = extensionDefintion.getPoint();
		InputNode node = extensionDefintion.getNode();

		// 根据扩展点定义实例化扩展
		ExtensionPoint extensionPoint = extensionPointMap.get(point);
		String defintionClassName = extensionPoint.getConfigClass();
		try {
			Class<Extension> clazz;
			clazz = ClassUtils.getClass(defintionClassName);
			Extension extension = serializer.read(clazz, node);

			getExtensions(point).add(extension);
		} catch (Exception e) {
			getExtensions(point).add(new ErrorExtensionConfig(point, e, node));
			logger.error("extension load error,point={},provider={}", point,
					provider.getKey());
			e.printStackTrace();
		}
	}

	/**
	 * 
	 * @see org.tangram.component.ComponentManager#getAllComponent()
	 */
	public List<Component> getAllComponent() {
		List<Component> all = new ArrayList<Component>();
		for (Component component : componentIndexMap.values()) {
			all.add(component);
		}
		return all;
	}

	/**
	 * 
	 * @see org.tangram.component.ComponentManager#getComponent(java.lang.String,
	 *      java.lang.String, java.lang.String)
	 */
	public Component getComponent(String groupId, String componentId) {
		String key = getKey(groupId, componentId);
		return componentIndexMap.get(key);
	}

	public ExtensionPoint getExtensionPoint(String point) {
		return extensionPointMap.get(point);
	}

	public List<Extension> getExtensions(String point) {
		if (extensions.get(point) == null)
			extensions.put(point, new ArrayList<Extension>());
		return extensions.get(point);
	}

	private String getKey(String groupId, String componentId) {
		return groupId + ":" + componentId;
	}
}
